package com.onepiece.requestproxy.requestproxy;

import com.onepiece.requestproxy.annotation.IRequestMethod;
import com.onepiece.requestproxy.annotation.RequestParams;
import com.onepiece.requestproxy.entity.enums.RequestConstants;
import com.onepiece.requestproxy.entity.enums.RequestResultEnum;
import com.onepiece.requestproxy.entity.enums.RequestTypeEnum;
import com.onepiece.requestproxy.entity.params.ParamsEntity;
import com.onepiece.requestproxy.util.BaseAnnotation;
import com.onepiece.requestproxy.util.JSONUtil;
import com.onepiece.requestproxy.util.ParamsComparator;
import com.onepiece.requestproxy.util.XMLParseUtil;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.*;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.annotation.Resource;

/**
 * 请求切面代理
 * 
 * @author JueYue 2014年2月20日--上午10:41:07
 */
public class RequestProxyHandler implements MethodInterceptor {

	private static final Logger logger = LoggerFactory
			.getLogger(RequestProxyHandler.class);

	private static IRequestMethod defaltMethodParams;

	@Resource(name = "httpClient")
	private HttpClient httpClient;

	public Object invoke(MethodInvocation methodInvocation) throws Throwable {
		try {
			IRequestMethod iRequest = methodInvocation.getMethod()
					.getAnnotation(IRequestMethod.class);
			if (iRequest == null) {
				iRequest = getDefaltMethodParams();
			}
			List<ParamsEntity> paramsEntityList = getParamsEntityList(
					methodInvocation.getArguments(),
					methodInvocation.getMethod(), iRequest);
			return getReturnObject(methodInvocation.getMethod(),
					getResponseResult(iRequest, paramsEntityList), iRequest);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private IRequestMethod getDefaltMethodParams() {
		if (defaltMethodParams != null) {
			return defaltMethodParams;
		}
		try {
			return BaseAnnotation.class.getMethod("requestMethod")
					.getAnnotation(IRequestMethod.class);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException("获取默认注解失败");
		}

	}

	private Object getReturnObject(Method method, String responseResult,
			IRequestMethod iRequest) {
		if (logger.isDebugEnabled()) {
			logger.debug("responseResult is  : {}", responseResult);
		}
		if (responseResult == null) {
			return null;
		}
		Class<?> returnType = method.getReturnType();
		if (returnType != null) {
			if (returnType.isAssignableFrom(String.class)) {
				return responseResult;
			}
			// 基本类型
			if (returnType.isPrimitive()) {
				Number number = JSONUtil.parseJson(responseResult,
						BigDecimal.class);
				if ("int".equals(returnType.getSimpleName())) {
					return number.intValue();
				}
				if ("long".equals(returnType.getSimpleName())) {
					return number.longValue();
				}
				if ("double".equals(returnType.getSimpleName())) {
					return number.doubleValue();
				}
			}
			// List类型
			if (returnType.isAssignableFrom(List.class)) {
				returnType = getListRealType(method.getGenericReturnType()
						.toString());
				return JSONUtil.parseJsonList(responseResult, returnType);
			}
			if (iRequest.result().equals(RequestResultEnum.JSON)) {
				return JSONUtil.parseJson(responseResult, returnType);
			} else {
				return XMLParseUtil.getEntity(responseResult, returnType);
			}
		}
		return null;
	}

	private Class<?> getListRealType(String genericReturnType) {
		String realType = genericReturnType.replace("java.util.List<", "")
				.replace(">", "");
		try {
			return Class.forName(realType);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}

	private String getResponseResult(IRequestMethod iRequest,
			List<ParamsEntity> paramsEntityList) throws IOException {
		if (iRequest.type().equals(RequestTypeEnum.POST)) {
			return getResponseResultByPost(iRequest, paramsEntityList);
		} else if (iRequest.type().equals(RequestTypeEnum.GET)) {
			return getResponseResultByGet(iRequest, paramsEntityList);
		} else if (iRequest.type().equals(RequestTypeEnum.WEBSERVICE)) {
			return getResponseResultByWebService(iRequest, paramsEntityList);
		} else if (iRequest.type().equals(RequestTypeEnum.REST)) {
			return getResponseResultByRest(iRequest, paramsEntityList);
		}
		return null;
	}

	/**
	 * Rest请求 2014年5月15日
	 * 
	 * @param iRequest
	 * @param paramsEntityList
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	private String getResponseResultByRest(IRequestMethod iRequest,
			List<ParamsEntity> paramsEntityList)
			throws UnsupportedEncodingException {
		Collections.sort(paramsEntityList, new ParamsComparator());
		String url = getBaseUrl(iRequest, paramsEntityList);
		if (url.endsWith("/"))
			url = url.substring(0, url.length() - 1);
		for (int i = 0; i < paramsEntityList.size(); i++) {
			if (!paramsEntityList.get(i).getName().equalsIgnoreCase("URL")) {
				url += "/" + paramsEntityList.get(i).getValue();
			}
		}
		return getResponseResultByUrl(iRequest, url);
	}

	/**
	 * 获取数据 post请求
	 * 
	 * @param iRequest
	 * @param paramsEntityList
	 * @return
	 */
	private String getResponseResultByPost(IRequestMethod iRequest,
			List<ParamsEntity> paramsEntityList) {
		PostMethod postMethod = new PostMethod(getBaseUrl(iRequest,
				paramsEntityList));
		postMethod.getParams().setSoTimeout(iRequest.connectTimeout() * 1000);
		postMethod.getParams().setContentCharset(iRequest.encode().getValue());
		postMethod.setRequestBody(getRequestBodyForPost(paramsEntityList,
				postMethod.getParams()));
		try {
			int status = httpClient.executeMethod(postMethod);
			if (status == HttpStatus.SC_OK) {
				return postMethod.getResponseBodyAsString();
			} else {
				if (logger.isErrorEnabled()) {
					logger.error("请求状态{},参数{},信息{}", status,
							JSONUtil.toJson(paramsEntityList),
							postMethod.getResponseBodyAsString());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放连接
			postMethod.releaseConnection();
		}
		return null;
	}

	private NameValuePair[] getRequestBodyForPost(
			List<ParamsEntity> paramsEntityList,
			HttpMethodParams httpMethodParams) {
		NameValuePair[] data = new NameValuePair[paramsEntityList.size()];
		for (int i = 0; i < paramsEntityList.size(); i++) {
			data[i] = new NameValuePair(paramsEntityList.get(i).getName(),
					paramsEntityList.get(i).getValue());
		}
		return data;
	}

	/**
	 * 获取数据 webserver请求
	 * 
	 * @param iRequest
	 * @param paramsEntityList
	 * @return
	 */
	private String getResponseResultByWebService(IRequestMethod iRequest,
			List<ParamsEntity> paramsEntityList) {
		PostMethod postMethod = new PostMethod(getBaseUrl(iRequest,
				paramsEntityList));
		postMethod.getParams().setSoTimeout(iRequest.connectTimeout() * 1000);
		postMethod.getParams().setContentCharset(iRequest.encode().getValue());
		postMethod
				.setRequestEntity(getRequestBodyForWebservice(paramsEntityList));
		try {
			int status = httpClient.executeMethod(postMethod);
			if (status == HttpStatus.SC_OK) {
				return postMethod.getResponseBodyAsString();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放连接
			postMethod.releaseConnection();
		}
		return null;
	}

	private RequestEntity getRequestBodyForWebservice(
			List<ParamsEntity> paramsEntityList) {
		String soapXml = getSoapXml(paramsEntityList);
		byte bytes[] = null;
		if (soapXml == null) {
			bytes = JSONUtil.toJson(paramsEntityList).getBytes();// 把字符串转换为二进制数据
		} else {
			bytes = soapXml.getBytes();
		}
		InputStream inputStream = new ByteArrayInputStream(bytes, 0,
				bytes.length);
		RequestEntity requestEntity = new InputStreamRequestEntity(inputStream,
				bytes.length, "application/soap+xml; charset=utf-8");
		return requestEntity;
	}

	private String getSoapXml(List<ParamsEntity> paramsEntityList) {
		for (int i = 0; i < paramsEntityList.size(); i++) {
			if (paramsEntityList.get(i).getName().equals(RequestConstants.SOAP)) {
				return paramsEntityList.get(i).getValue();
			}
		}
		return null;
	}

	/**
	 * 获取数据get请求
	 * 
	 * @param iRequest
	 * @param paramsEntityList
	 * @return
	 */
	private String getResponseResultByGet(IRequestMethod iRequest,
			List<ParamsEntity> paramsEntityList) throws IOException {
		String url = getParamsForGet(getBaseUrl(iRequest, paramsEntityList),
				paramsEntityList, iRequest);
		return getResponseResultByUrl(iRequest, url);
	}

	private String getResponseResultByUrl(IRequestMethod iRequest, String url) {
		if (logger.isDebugEnabled()) {
			logger.debug("请求URL:{}", url);
		}
		GetMethod getMethod = new GetMethod(url);
		getMethod.getParams().setSoTimeout(iRequest.connectTimeout() * 1000);
		getMethod.getParams().setContentCharset(iRequest.encode().getValue());
		try {
			int status = httpClient.executeMethod(getMethod);
			if (status == HttpStatus.SC_OK) {
				return getMethod.getResponseBodyAsString();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放连接
			getMethod.releaseConnection();
		}
		return null;
	}

	/**
	 * 拼接URL
	 * 
	 * @param paramsEntityList
	 * @param iRequest
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	private String getParamsForGet(String baseUrl,
			List<ParamsEntity> paramsEntityList, IRequestMethod iRequest)
			throws UnsupportedEncodingException {
		StringBuilder stringBuilder = new StringBuilder(baseUrl);
		boolean isAddAnd = false;
		if (baseUrl.contains("?")) {
			isAddAnd = true;
		}
		for (int i = 0, le = paramsEntityList.size(); i < le; i++) {
			if (paramsEntityList.get(i).getName().equalsIgnoreCase("URL")) {
				continue;
			}
			if (isAddAnd) {
				stringBuilder.append("&");
			} else {
				stringBuilder.append("?");
				isAddAnd = true;
			}
			stringBuilder.append(paramsEntityList.get(i).getName());
			stringBuilder.append("=");
			stringBuilder.append(paramsEntityList.get(i).getValue());
		}
		return stringBuilder.toString();
	}

	/**
	 * 获取基础URL
	 * 
	 * @param iRequest
	 * @param paramsEntityList
	 * @return
	 */
	private String getBaseUrl(IRequestMethod iRequest,
			List<ParamsEntity> paramsEntityList) {
		if (iRequest != null && StringUtils.isNotBlank(iRequest.url())) {
			return iRequest.url();
		}
		for (int i = 0, le = paramsEntityList.size(); i < le; i++) {
			if (paramsEntityList.get(i).getName().equalsIgnoreCase("URL")) {
				return (String) paramsEntityList.get(i).getValue();
			}
		}
		throw new RuntimeException("没有找到请求地址");
	}

	private List<ParamsEntity> getParamsEntityList(Object[] parameters,
			Method method, IRequestMethod iRequest) throws Exception {
		Annotation[][] annotations = method.getParameterAnnotations();
		List<ParamsEntity> list = new ArrayList<ParamsEntity>();
		ParamsEntity entity;
		for (int i = 0, le = parameters.length; i < le; i++) {
			entity = new ParamsEntity();
			for (int k = 0, ale = annotations[i].length; k < ale; k++) {
				if (annotations[i][k] instanceof RequestParams) {
					entity.setName(((RequestParams) annotations[i][k]).value());
					entity.setOrder(((RequestParams) annotations[i][k]).order());
				}
			}
			if (StringUtils.isEmpty(entity.getName())) {
				throw new RuntimeException("这个参数没有注释名称:"
						+ parameters[i].toString());
			}
			if (!entity.getName().equalsIgnoreCase("url")) {
				if (iRequest.isTranscoding()) {
					entity.setValue(URLEncoder.encode(String
							.valueOf(parameters[i]), iRequest.encode()
							.getValue()));
				} else {
					entity.setValue(String.valueOf(parameters[i]));
				}
			} else {
				entity.setValue(String.valueOf(parameters[i]));
			}
			list.add(entity);
		}
		return list;
	}

	public HttpClient getHttpClient() {
		return httpClient;
	}

	public void setHttpClient(HttpClient httpclient) {
		this.httpClient = httpclient;
	}

}
